/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Portions created by the Initial Developer are
 * Copyright (C) 2012 the Initial Developer.
 * All Rights Reserved.
 *
 * The Original Code is this.
 *
 * The Initial Developer of the Original Code is the author below.
 */
/**
 * @file SharedContext.cpp
 * @brief auto generated.
 * @author K.Ozaki
 * @date 2011/07/21
 */

#include "semi/SharedContext.h"

namespace semi {

std::string SharedContext::current_mem_names = "semi.CurrentContext";
int SharedContext::lock_timeout = -1;

SharedContext::SharedContext(std::string fname, int fsize, bool lk, bool st) {
    this->init(fname, fsize, lk, st);
}

void SharedContext::init(std::string fname, int fsize, bool lk, bool st) {

    this->fileName = fname;
    this->stable = st;
    this->addToList(fname, fsize, st);
    //    this->sMem = new semi::posix::SharedMemory(fname, fsize);
    //    this->semlk = new semi::posix::Semaphore(this->fileName);
    this->sMem.init(fname, fsize);
    this->semlk.init(this->fileName);
    this->onLock = false;
    this->toLock = false;
    this->setLockMode(lk);
}

SharedContext::~SharedContext() {
    this->end();
}

void SharedContext::addToList(std::string filename, int filesize, bool stable) {
    semi::posix::SharedMemory
            mems(current_mem_names, sizeof(SharedMemoryNames));
    SharedMemoryNames* nms = (SharedMemoryNames*) mems.get();
    int idx = 0;
    for (; idx < (sizeof(nms->names) / sizeof(nms->names[0])); idx++) {
        std::string nm = (nms->names[idx]);
        if (0 == nm.length() || nm == filename)
            break;
    }
    if (127 < idx) {
        logg::Logger::getCurrentLogger().error(
                "SharedContext over limit index(127)!");
        //        std::cout << "  SharedContext over limit " << std::endl;
    }
    int len = sizeof(nms->names[0]);
    memset(nms->names[idx], 0, len);
    if (filename.length() < len)
        len = filename.length();
    strncpy(nms->names[idx], filename.c_str(), len);
    nms->sizes[idx] = filesize;

    if (stable)
        nms->scope[idx] = '0';
    else
        nms->scope[idx] = '1';
    // std::cout << "SharedContext::SharedContext count" << nms->count << std::endl;
    nms = 0;
    mems.end();
}

void SharedContext::removeFromList(std::string filename) {
    semi::posix::SharedMemory
            mems(current_mem_names, sizeof(SharedMemoryNames));
    SharedMemoryNames* nms = (SharedMemoryNames*) mems.get();
    for (int idx = 0; idx < (sizeof(nms->names) / sizeof(nms->names[0])); idx++) {
        if (0 == strncmp(filename.c_str(), nms->names[idx], filename.length())) {
            memset(nms->names[idx], 0, sizeof(nms->names[idx]));
            nms->sizes[idx] = 0;
            nms->scope[idx] = '0';
        }
    }
    nms = 0;
    mems.end();
}

void SharedContext::signal_alarm_handler(int param) {
    std::cout << "  SharedContext::signal_alarm_handler Rise timeout"
            << std::endl;
    throw std::logic_error("Rise timeout for waiting semapho.");
}

void SharedContext::lock() {

    if (this->toLock && !this->onLock) {

        logg::Logger logg = logg::Logger::getCurrentLogger();

        if (0 == SharedContext::lock_timeout)
            if (this->semlk.getvalue() <= 0) {
                throw std::logic_error("Rise timeout for waiting semapho.");
            } else {
                this->semlk.wait();
                this->semlk.close();
                this->onLock = true;
                this->toLock = true;
                logg.info("SharedContext::get lock start.   "
                        + this->sMem.filename);
                return;
            }

        // timeout
        try {

            if (logg.isTraceEnabled())
                std::cout << "    SharedContext::get lock start sem.name:"
                        << this->semlk.name << " wait:"
                        << SharedContext::lock_timeout << " semlk.val:"
                        << this->semlk.getvalue() << std::endl;

            signal(SIGALRM, SharedContext::signal_alarm_handler);
            alarm(SharedContext::lock_timeout);

            this->semlk.wait();
            alarm(0);
            this->semlk.close();
            this->onLock = true;
            this->toLock = true;
            logg.info("SharedContext::get lock start.   " + this->sMem.filename);
            return;
        }
        // from handler
        catch (std::logic_error lx) {
            alarm(0);
            std::cout << "    SharedContext::logic_error Rise timeout"
                    << std::endl;
            // this->semlk.postAll();
            this->semlk.close();
            // Exception thrown for Logic.
            logg.info(this->sMem.filename + " fail to lock. Exception thrown. "
            //          + std::string(lx.what())
            );
            throw lx;
        }
    }

    // std::cout << "  SharedContext::lock end" << std::endl;
}

void SharedContext::unLock() {

    if (this->onLock) {
        logg::Logger logg = logg::Logger::getCurrentLogger();
        std::string str = "SharedContext::end lock release. "
                + this->sMem.filename;
        logg.info(str);
        this->semlk.post();
        this->semlk.close();
        // std::cout << "  SharedContext::end semlk.close " << std::endl;

        this->onLock = false;
    }
}

void SharedContext::read(void * ret, bool lk) {

    //    void * ret = malloc(this->sMem.fsize);

    if (lk)
        this->lock();

    void * ptr = this->sMem.get(PROT_READ);

    //    free(ret);
    memcpy(ret, ptr, this->sMem.fsize);

    this->sMem.end();
    //    return ret;
}

//void * SharedContext::read() {
//    return this->read(false);
//}

void SharedContext::get(void * ret) {
    return this->read(ret, true);
}

void SharedContext::set(void * st) {

    void* dist = malloc(this->sMem.fsize);
    memcpy(dist, st, this->sMem.fsize);
    //    std::cout << "dist " << std::string((char*) dist) << " " << st << std::endl;

    this->lock();

    //    std::cout << "    SharedContext::set dist " << std::string((char*) dist)
    //            << std::endl;

    void* pnt = this->sMem.get(PROT_WRITE);
    // std::cout << "  SharedContext::set :" << (int) pnt << std::endl;
    memcpy(pnt, dist, this->sMem.fsize);
    // this->sMem.flush();
    this->sMem.end();
    free(dist);
}

void SharedContext::setLockMode(bool lk) {
    if ((!this->toLock) && (lk)) {
        if (SharedContext::lock_timeout < 0) {
            semi::conf::Configurator cnfg;
            semi::conf::Configurator::BaseConfig bcnf = cnfg.getBaseConfig();
            SharedContext::lock_timeout = bcnf.lock_timeout;
            if (SharedContext::lock_timeout < 0)
                SharedContext::lock_timeout = 0;
        }
        if (logg::Logger::getCurrentLogger().isTraceEnabled())
            std::cout << "    SharedContext::setLockMode lock "
                    << this->semlk.name << " (timeout:"
                    << SharedContext::lock_timeout << ")" << std::endl;
    }
    this->toLock = lk;
}

void SharedContext::end() {

    this->unLock();
    this->sMem.end();

    return;
}

void SharedContext::terminate() {

    std::cout << "  SharedContext::terminate lock " << this->semlk.name
            << std::endl;

    this->unLock();
    this->removeFromList(this->fileName);
    this->sMem.terminate();
}

//bool SharedContext::flush(void *sdata) {
//    return this->sMem.flush(sdata);
//}

void SharedContext::releaseAllLocks() {

    semi::posix::SharedMemory
            mems(current_mem_names, sizeof(SharedMemoryNames));
    SharedMemoryNames* nms = (SharedMemoryNames*) mems.get(PROT_READ);

    std::cout << "  SharedContext::releaseAllLocks " << std::endl;

    for (int i = 0; i < (sizeof(nms->names) / sizeof(nms->names[0])); i++) {
        std::string nm = (nms->names[i]);
        if (0 == nm.length() && 0 == nms->sizes[i])
            continue;
        //        if ('1' == nms->scope[i]) // session
        //            continue;
        semi::posix::Semaphore sem(nm);
        sem.terminate();
    }
    mems.end();
}

std::string SharedContext::toString() {

    std::stringstream ss;
    ss << std::endl << current_mem_names << std::endl;
    int cnt = 0;

    semi::posix::SharedMemory
            mems(current_mem_names, sizeof(SharedMemoryNames));
    SharedMemoryNames* nms = (SharedMemoryNames*) mems.get(PROT_READ);

    for (int i = 0; i < (sizeof(nms->names) / sizeof(nms->names[0])); i++) {
        std::string nm = (nms->names[i]);
        if (0 == nm.length() && 0 == nms->sizes[i])
            continue;
        if ('1' == nms->scope[i]) // session
            continue;
        ss << "    " << i << " " << nm << " size:" << nms->sizes[i];
        // ss << " scope:" << nms->scope[i];
        semi::posix::Semaphore sm(nm);
        ss << " lock:" << sm.getvalue() * -1 + 1;
        sm.close();
        ss << std::endl;
        cnt++;
    }
    ss << "    display count:" << cnt << std::endl;
    cnt = 0;
    ss << "  session context" << std::endl;
    for (int i = 0; i < (sizeof(nms->names) / sizeof(nms->names[0])); i++) {
        std::string nm = (nms->names[i]);
        if (0 == nm.length() && 0 == nms->sizes[i])
            continue;
        if ('0' == nms->scope[i]) // stable
            continue;
        ss << "    " << i << " " << nm << " size:" << nms->sizes[i];
        // ss << " scope:" << nms->scope[i];
        semi::posix::Semaphore sm(nm);
        ss << " lock:" << sm.getvalue() * -1 + 1;
        sm.close();
        ss << std::endl;
        cnt++;
    }
    ss << "    display count:" << cnt << std::endl;
    mems.end();

    return ss.str();
}

}// semi
